--------------Bake & Taste-------------
A 4am crack                  2017-08-04
---------------------------------------

Name: Bake & Taste
Genre: educational
Year: 1986
Publisher: Mindplay
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: Pronto-DOS
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  fails on last pass

Locksmith Fast Disk Backup
  unable to read track $20
  copy boots to title screen then hangs

EDD 4 bit copy (no sync, no count)
  copy boots to title screen then says
    "DISK ERROR  35W" and hangs

Copy ][+ nibble editor
  track $20 is almost entirely sync
  bytes, with the occasional $D5 nibble
  (almost certainly a protection track)

Disk Fixer
  entire disk is standard except T20
  T00,S00 -> DOS 3.3 bootloader
  T02,S00 -> disk volume name is
    "PRONTO-DOS"
  T01,S07 -> startup program is "HELLO"
  standard DOS 3.3 disk catalog on T11

Why didn't any of my copies work?
  specially formatted nibble sequence
  on track $20, designed to fool even
  the best bit copiers

Next steps:

  1. Find the protection check that is
     reading track $20
  2. Disable it
  3. Declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
  In Which We Print A Bunch of Things
      And Crash A Bunch Of Things
       And Print The Crashy Bits
       And Crash The Printy Bits


Booting my non-working copy and
pressing <Ctrl-C> drops me to a working
BASIC prompt with DOS in memory.

]PR#6
<Ctrl-C>

]CATALOG

PRONTO-DOS V254

 B 136 FISH
 B 011 MS1
 B 020 MS2
 B 031 MS3
 B 028 MS4
 B 029 MS5
 B 021 MS6
 B 021 MS7
 B 045 MS8
 B 025 RUNTIME.CG
 B 034 MAIN
 B 034 TITLE
 T 027 RECIPE.FILE
 T 003 INGREDIENTS
 T 006 CLUE
 T 002 YOU
 B 003 SCORE
 A 002 HELLO

]LIST

 5  POKE 1012,0
 10  PRINT "BLOAD RUNTIME.CG" +  CHR$
     (13) + "BRUN MS1"

]BLOAD RUNTIME.CG
]BLOAD MS1
]CALL -151

*AA72.AA73        ; BLOAD start address

AA72- DE 42

*42DEL

42DE-   20 03 08    JSR   $0803
42E1-   00          BRK
42E2-   00          BRK
42E3-   E9 4B       SBC   #$4B
42E5-   0F          ???
42E6-   41 3B       EOR   ($3B,X)
42E8-   42          ???
42E9-   DD 42 E4    CMP   $E442,X
42EC-   4B          ???
42ED-   E9 4B       SBC   #$4B
42EF-   E9 4B       SBC   #$4B
42F1-   00          BRK
42F2-   4C 00 43    JMP   $4300

On the theory that the JMP instruction
at $42F2 is a real instruction and all
the nonsense-looking opcodes before it
are parameters to the subroutine at
$0803, I'm going to set a breakpoint
at $42F2 and see what happens.

*42F2:00
*42DEG

42F4-    A=00 X=B8 Y=00 P=32 S=F9
*

Oh good, it crashed exactly where I
wanted it to crash.

*4300L

4300-   20 C5 0E    JSR   $0EC5
4303-   F5 42       SBC   $42,X

That looks like an address ($42F5)
being passed to the subroutine at
$0EC5.

*42F5.

42F5-                0A 04 4D
42F8- 41 58 46 49 4C 45 53 31

At least some of that looks like ASCII
characters.

*FC58G N 400<42F5.42FFM

JDMAXFILES1

A length byte, <Ctrl-D>, a DOS command,
and thou. Everything you need for a
romantic evening at home.

4305-   20 EE 0E    JSR   $0EEE
4308-   20 FB DA    JSR   $DAFB
430B-   20 A3 49    JSR   $49A3
430E-   20 D8 08    JSR   $08D8
4311-   20 C7 49    JSR   $49C7
4314-   20 3E 17    JSR   $173E
4317-   20 6D 49    JSR   $496D
431A-   20 54 14    JSR   $1454
431D-   20 F0 4A    JSR   $4AF0
4320-   20 3E 17    JSR   $173E
4323-   20 6D 49    JSR   $496D
4326-   20 54 14    JSR   $1454
4329-   4C 39 43    JMP   $4339

*4329:0

*4300G

432B-    A=20 X=20 Y=00 P=33 S=F7

Well I don't know what I'm doing, but
I know I haven't found the protection
check yet.

*4339L

4339-   20 C5 0E    JSR   $0EC5
433C-   2C 43

Same pattern as before -- calling a
subroutine with a 2-byte address of a
string to "print" (a.k.a. execute as
a DOS command).

*432C

432C- 0C

*FC58G N 400<432D.4338M

DBLOAD TITLE

*433EL

433E-   20 EE 0E    JSR   $0EEE
4341-   20 FB DA    JSR   $DAFB
4344-   20 D5 4A    JSR   $4AD5
4347-   20 38 17    JSR   $1738
434A-   20 88 49    JSR   $4988
434D-   20 A1 14    JSR   $14A1
4350-   4C 5E 43    JMP   $435E

*4350:0

*4339G
...loads and displays the title page,
then displays the crash register line
in hi-res over the title graphic,
which is too awesome to describe in
words...

<Ctrl-Reset> gets me back to text mode.

                   ~

               Chapter 2
      In Which We Find The Thing


I'm going to skip ahead because there
are a lot of these commands-via-string.
They all use the same pattern.

> FILE FISH
> FILE FISH,R24,B96,A$8D86,L3354
> FILE FISH,R38,B123,A$300,L175
> FILE FISH,R37,B121,A$4000,L259
> CLOSE
> OPEN RECIPE.FILE,L212
> READ RECIPE.FILE,R
> CLOSE
> BLOAD SCORE,A$8100

That brings us to here:

*458BL

458B-   20 EE 0E    JSR   $0EEE
458E-   20 FB DA    JSR   $DAFB
4591-   20 C3 4A    JSR   $4AC3
4594-   20 3B 17    JSR   $173B
4597-   4C AC 45    JMP   $45AC

Setting $4597 to #$00 never crashes; it
just hangs. I have found the protection
check!

So where is it?

<Ctrl-Reset> gets me back to a working
DOS prompt. Let's see what's in that
"SCORE" file at $8100.

]CALL -151

*8100L

; get boot slot (x16)
8100-   A6 2B       LDX   $2B

; save some zero page locations on the
; stack
8102-   A5 FD       LDA   $FD
8104-   48          PHA
8105-   A5 FE       LDA   $FE
8107-   48          PHA

; get the address of the RWTS parameter
; table
8108-   20 E3 03    JSR   $03E3

810B-   84 FD       STY   $FD
810D-   85 FE       STA   $FE

; (RWTS)+$04 is track -> #$20
; Hey, that's the protection track!
810F-   A0 04       LDY   #$04
8111-   A9 20       LDA   #$20
8113-   91 FD       STA   ($FD),Y

; (RWTS)+$0C is command -> #$00 = seek
8115-   A0 0C       LDY   #$0C
8117-   A9 00       LDA   #$00
8119-   91 FD       STA   ($FD),Y

; slow IIgs down to slow speed (this is
; probably superstitious -- the IIgs
; will operate at slow speed while the
; drive motor is on)
811B-   AD 36 C0    LDA   $C036
811E-   29 7F       AND   #$7F
8120-   8D 36 C0    STA   $C036

; patch RWTS in memory to exit without
; turning off the drive motor
8123-   A9 60       LDA   #$60
8125-   8D 4D BE    STA   $BE4D

; execute the seek to track $20 (and
; leave the drive motor running)
8128-   20 E3 03    JSR   $03E3
812B-   20 D9 03    JSR   $03D9

; restore the RWTS patch
812E-   A9 BD       LDA   #$BD
8130-   8D 4D BE    STA   $BE4D

; branch if the RWTS seek failed for
; some reason
8133-   B0 65       BCS   $819A

; find a $D5 nibble
8135-   BD 8C C0    LDA   $C08C,X
8138-   10 FB       BPL   $8135
813A-   48          PHA
813B-   68          PLA
813C-   C9 D5       CMP   #$D5
813E-   D0 F5       BNE   $8135
8140-   A0 00       LDY   #$00
8142-   8C CD 81    STY   $81CD

; look at nibbles until the next $D5
8145-   BD 8C C0    LDA   $C08C,X
8148-   10 FB       BPL   $8145
814A-   C9 D5       CMP   #$D5
814C-   F0 0F       BEQ   $815D
814E-   C9 F7       CMP   #$F7
8150-   D0 01       BNE   $8153

; increment Y whenever we find $F7, and
; use the nibble value itself to
; decrement $81CD (will be either $F7
; or $FF, since those are the only
; nibbles on the entire track)
8152-   C8          INY
8153-   18          CLC
8154-   6D CD 81    ADC   $81CD
8157-   8D CD 81    STA   $81CD
815A-   4C 45 81    JMP   $8145

; execution continues here (from the
; "BEQ" at $814C) when we hit the next
; $D5 nibble -- check the $F7 counter
; in the Y register and start over if
; it's still zero
815D-   98          TYA
815E-   F0 E0       BEQ   $8140

; otherwise skip over all the sync
; nibbles ($FF)
8160-   BD 8C C0    LDA   $C08C,X
8163-   10 FB       BPL   $8160
8165-   48          PHA
8166-   68          PLA
8167-   C9 FF       CMP   #$FF
8169-   F0 F5       BEQ   $8160

; if first non-$FF nibble is $D5, fail
816B-   C9 D5       CMP   #$D5
816D-   F0 35       BEQ   $81A4

; skip over exactly 5 nibbles
816F-   A0 05       LDY   #$05
8171-   BD 8C C0    LDA   $C08C,X
8174-   10 FB       BPL   $8171
8176-   48          PHA
8177-   68          PLA
8178-   88          DEY
8179-   D0 F6       BNE   $8171

; skip over all the sync nibbles
817B-   BD 8C C0    LDA   $C08C,X
817E-   10 FB       BPL   $817B
8180-   48          PHA
8181-   68          PLA
8182-   C9 FF       CMP   #$FF
8184-   F0 F5       BEQ   $817B

; if first non-$FF nibble is $D5, fail
8186-   C9 D5       CMP   #$D5
8188-   D0 1A       BNE   $81A4

; if next nibble is NOT $FF, fail
818A-   BD 8C C0    LDA   $C08C,X
818D-   10 FB       BPL   $818A
818F-   C9 FF       CMP   #$FF
8191-   D0 11       BNE   $81A4

; check the accumulated value in $81CD
; (from summing the $F7 and $FF nibbles
; earlier) against the "correct" total,
; fail if the sum is incorrect
8193-   AD CD 81    LDA   $81CD
8196-   C9 10       CMP   #$10
8198-   D0 0A       BNE   $81A4

; finally turn off the drive motor,
; restore zero page, and exit
819A-   BD 88 C0    LDA   $C088,X
819D-   68          PLA
819E-   85 FE       STA   $FE
81A0-   68          PLA
81A1-   85 FD       STA   $FD
81A3-   60          RTS

; failure path is here (from many
; different branches) -- wipe the
; protection code from memory
81A4-   A2 3C       LDX   #$3C
81A6-   A9 A0       LDA   #$A0
81A8-   99 FF 81    STA   $81FF,Y
81AB-   C8          INY
81AC-   D0 FA       BNE   $81A8
81AE-   EE AA 81    INC   $81AA
81B1-   CA          DEX
81B2-   D0 F4       BNE   $81A8

; clear the screen and print the
; encrypted error message
; ("DISK ERROR  35W")
81B4-   AD 8A C0    LDA   $C08A
81B7-   20 2F FB    JSR   $FB2F
81BA-   20 58 FC    JSR   $FC58
81BD-   A0 0E       LDY   #$0E
81BF-   B9 CE 81    LDA   $81CE,Y
81C2-   49 BB       EOR   #$BB
81C4-   99 B4 05    STA   $05B4,Y
81C7-   88          DEY
81C8-   10 F5       BPL   $81BF

; disable interrupts and loop forever
81CA-   78          SEI
81CB-   30 FE       BMI   $81CB

That's where we ended up on our non-
working EDD bit copy. Excellent!

                   ~

               Chapter 3
   In Which We Go To Patch The Thing
   And Can't Find The Thing To Patch


The solution looks trivial: put an RTS
at the beginning of the protection
check. It has no side effects; if it
fails, it never returns, and when it
returns, it doesn't even set the carry
bit for the caller to check. So we can
just unconditionally return.

Except...

Looking at the "SCORE" file on disk,
this code isn't there.

]PR#6
...
]BLOAD SCORE,A$8100
]CALL -151

*8100L

8100-   03          ???
8101-   8E 00 58    STX   $5800
8104-   ED 00 5B    SBC   $5B00
8107-   ED 85 46    SBC   $4685
810A-   A6 21       LDX   $21
810C-   58          CLI
810D-   20 5B 05    JSR   $055B
8110-   A1 0C       LDA   ($0C,X)
8112-   85 34       STA   $34
8114-   58          CLI

Um.

However! Looking at the file in my
trusty sector editor, I happened upon
real code on the very next page.

*8200L

8200-   A0 00       LDY   #$00
8202-   B9 00 81    LDA   $8100,Y
8205-   49 A5       EOR   #$A5
8207-   99 00 81    STA   $8100,Y
820A-   C8          INY
820B-   D0 F5       BNE   $8202
820D-   4C 00 81    JMP   $8100

Well that explains everything. The code
is decrypted with a simple XOR loop and
jumped to. So this is where I can put
an "RTS".

On the offhand chance that some later
code checks whether the protection code
is actually in memory at $8100, I'm
going to let the decryption loop run
before returning.

Turning again to my trusty Disk Fixer
sector editor, I follow the "SCORE"
file and find this code on track $03.

T03,S04,$11: 4C -> 60

]PR#6
...works, and it is delicious...

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1352
------------------EOF------------------
